package com.moonshineproject.plugin.actionscript.as3parser.parsers
{
	import __AS3__.vec.Vector;
	
	import com.moonshineproject.plugin.actionscript.as3parser.completers.AccessLevels;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.BaseVO;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.FunctionVO;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.MemberBase;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.MutatorVO;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.ParameterVO;
	import com.moonshineproject.plugin.actionscript.as3parser.vo.VariableVO;
	
	import org.as3commons.asblocks.parser.api.IParserNode;

	public class DefinitionParser
	{
		protected var ast:IParserNode;
		protected var imports:ImportParser;

		public var className:String;
		public var isInterface:Boolean;

		public var definitions:Vector.<MemberBase> = new Vector.<MemberBase>();

		public function DefinitionParser(ast:IParserNode, imp:ImportParser)
		{
			this.ast = ast;
			this.imports = imp;
		}

		public function resolveTopDefinitions():void
		{
			var classNode:IParserNode = recurseFindClassContent(ast);
			findDefinitions(classNode);
		}

		public function resolveBlockDefinitions():void
		{
			findBlockDefinitions(ast);
		}

		public function getMember(property:String):MemberBase
		{
			for (var i:int = 0; i < definitions.length; i++)
			{
				if (definitions[i].name == property)
					return definitions[i];
			}

			return null;
		}

		protected function recurseFindClassContent(searchNode:IParserNode):IParserNode
		{
			for (var i:int = 0; i < searchNode.children.length; i++)
			{
				var node:IParserNode = searchNode.children[i];
				if (node.kind == "package")
				{
					if (node.children)
						return recurseFindClassContent(node);
				}
				else if (node.kind == "content")
				{
					if (node.children)
						return recurseFindClassContent(node);
				}
				else if (node.kind == "class")
				{
					findClassName(node);
					if (node.children)
						return recurseFindClassContent(node);
				}
				else if (node.kind == "interface")
				{
					isInterface = true;
					findClassName(node);
					if (node.children)
						return recurseFindClassContent(node);
				}
			}

			return searchNode;
		}

		protected function findClassName(searchNode:IParserNode):void
		{
			if (!searchNode.children)
				return;

			for (var i:int = 0; i < searchNode.children.length; i++)
			{
				var node:IParserNode = searchNode.children[i];
				if (node.kind == "name")
				{
					this.className = node.stringValue;
					break; // just in case node is found early
				}
			}
		}

		protected function findDefinitions(searchNode:IParserNode):void
		{
			for (var i:int = 0; i < searchNode.children.length; i++)
			{
				var node:IParserNode = searchNode.children[i];
				if (node.kind == "field-list")
				{
					parseVariable(node, VariableVO, definitions);
					continue;
				}
				else if (node.kind == "function")
				{
					parseFunction(node);
					continue;
				}
			}
		}
		
		protected function findBlockDefinitions(searchNode:IParserNode):void
		{
			for (var i:int = 0; i < searchNode.children.length; i++)
			{
				var node:IParserNode = searchNode.children[i];
				if (node.kind == "dec-list")
				{
					if (node.children && node.children.length > 1)
					{
						parseVariable(node, VariableVO, definitions);
						continue;	
					}
				}
			}
		}

		// Also used to parse parameters for functions
		protected function parseVariable(variableNode:IParserNode, classType:Class, collection:Object):void
		{
			var variableOrParamater:BaseVO = new classType() as BaseVO;
			
			for (var i:int = 0; i < variableNode.children.length; i++)
			{
				var node:IParserNode = variableNode.children[i];
				if (node.kind == "parameter")
				{
					// Dive one level down again
					parseVariable(node, classType, collection);
				}
				else if (node.kind == "name-type-init")
				{
					// Extract name and type
					for each (var subnode:IParserNode in node.children)
					{
						if (subnode.kind == "type")
						{
							variableOrParamater.type = subnode.stringValue;
						}
						else if (subnode.kind == "name")
						{
							variableOrParamater.name = subnode.stringValue;
						}
					}					
				}
				else if (node.kind == "mod-list" && node.children)
				{
					// Extract access modifier (variables only)
					var kind:String = node.children[0].stringValue;
					VariableVO(variableOrParamater).accessor = AccessLevels.stringToLevel(kind);
				}
			}
			
			// Get fully qualified type name via imports
			variableOrParamater.typePackage = imports.getPackageNameForType(variableOrParamater.type);
			
			// Add it!
			collection.push(variableOrParamater);
		}

		protected function parseFunction(functionNode:IParserNode):void
		{
			var name:String;
			var returnType:String;

			var method:FunctionVO = new FunctionVO();
						
			var mutator:MutatorVO;
			var mutatorType:String

			for (var i:int = 0; i < functionNode.children.length; i++)
			{
				var node:IParserNode = functionNode.children[i];
				if (node.kind == "name")
				{
					name = node.stringValue;
					method.name = name;
								
				}
				else if (node.kind == "type" || node.kind == "")
				{
					method.type = node.stringValue;
				}
				else if (node.kind == "mod-list" && node.children)
				{
					var kind:String = node.children[0].stringValue;
					method.accessor = AccessLevels.stringToLevel(kind);
				}
				else if (node.kind == "parameter-list" && node.children)
				{
					parseVariable(node, ParameterVO, method.parameters);
				
				}else if(node.kind == "accessor-role" && node.children){
					
					// Kind will be of type set/get
					mutatorType = node.children[0].kind;					
				}
			}

			
			method.typePackage = imports.getPackageNameForType(method.type);
			
			
			// If we have a getter or setter, move the method to it's setter or getter
			if (mutatorType)
			{	
				mutator = getMember(name) as MutatorVO;
				if (!mutator)
				{
					mutator = new MutatorVO();
				}
				if (mutatorType =="set")
				{
					mutator.setter = method;
				}
				else
				{
					mutator.getter = method;
				}
				
				mutator.accessor = method.accessor;
				mutator.name = name;
			}
			
			// Is this a getter or setter, and have it been added already?
			if (mutator && definitions.indexOf(mutator) == -1)
			{		
				definitions.push(mutator);
			}
			else if (!mutator)
			{
				definitions.push(method);
			}
			
		}
		

	}
}